﻿using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using LINQPad.OpenAccess.Data;
using LINQPad.OpenAccess.Helpers;

namespace LINQPad.OpenAccess.Services
{
    /// <summary>
    /// <see cref="IApplicationConfigService"/>
    /// </summary>
    public class ApplicationConfigService : IApplicationConfigService
    {
        private const string IgnoredConnectionName = "LocalSqlServer";

        #region Private

        #region - Functions

        /// <summary>
        /// Tries to get the predefined connections from the specified application config file.
        /// </summary>
        /// <param name="appConfigFilePath">The app config file path.</param>
        /// <param name="predefinedConnections">The predefined connections.</param>
        /// <param name="throwOnError">set to <c>true</c> to throw errors instead of suppressing them.</param>
        /// <returns>
        ///   <c>true</c> on success; otherwise, <c>false</c>.
        /// </returns>
        /// <exception cref="System.IO.FileNotFoundException">if no such file exists and <paramref name="throwOnError" /> is <c>true</c>.</exception>
        private bool TryGetPredefinedConnections( string appConfigFilePath, out PredefinedConnectionData[] predefinedConnections, bool throwOnError = false )
        {
            Guard.ArgumentNotNullOrEmpty( appConfigFilePath, "appConfigFilePath" );

            predefinedConnections = null;

            if ( !File.Exists( appConfigFilePath ) )
            {
                var error = String.Format( "No such application config file ! (File: {0})", appConfigFilePath );
                if ( throwOnError )
                    throw new FileNotFoundException( error, appConfigFilePath );
                Trace.TraceError( "[ApplicationConfigService]: {0}", error );
                return false;
            }

            var fileMap = new ExeConfigurationFileMap( ) { ExeConfigFilename = appConfigFilePath };
            try
            {
                var config = ConfigurationManager.OpenMappedExeConfiguration( fileMap, ConfigurationUserLevel.None );
                predefinedConnections = config.ConnectionStrings
                                              .ConnectionStrings
                                              .OfType<ConnectionStringSettings>( )
                                              .Where( c => !String.Equals( IgnoredConnectionName, c.Name ) )
                                              .Select( c => new PredefinedConnectionData( c.Name, c.ConnectionString, c.ProviderName ) )
                                              .ToArray( );
                return true;
            }
            catch ( ConfigurationErrorsException ex )
            {
                if ( throwOnError )
                    throw;
                Trace.TraceError
                        (
                            "[ApplicationConfigService]: Failed to load app.config file '{0}' !\nMessage: {1}\nBareMessage: {2}\nStacktrace: {3}"
                            , appConfigFilePath
                            , ex.Message
                            , ex.BareMessage
                            , ex.StackTrace
                        );
                return false;
            }
        }

        #endregion

        #endregion

        #region IApplicationConfigService Members

        /// <summary>
        /// Gets the predefined connections from the specified application config file.
        /// </summary>
        /// <param name="appConfigFilePath">The app config file path.</param>
        /// <returns>
        /// The predefined connections or an empty array.
        /// </returns>
        public PredefinedConnectionData[] GetPredefinedConnections( string appConfigFilePath )
        {
            Guard.ArgumentNotNullOrEmpty( appConfigFilePath, "appConfigFilePath" );

            PredefinedConnectionData[] connections;
            this.TryGetPredefinedConnections( appConfigFilePath, out connections, throwOnError: true );

            return connections ?? new PredefinedConnectionData[0];
        }
        /// <summary>
        /// Tries to get the predefined connections from the specified application config file.
        /// </summary>
        /// <param name="appConfigFilePath">The app config file path.</param>
        /// <param name="predefinedConnections">The predefined connections.</param>
        /// <returns>
        ///   <c>true</c> on success; otherwise, <c>false</c>.
        /// </returns>
        public bool TryGetPredefinedConnections( string appConfigFilePath, out PredefinedConnectionData[] predefinedConnections )
        {
            Guard.ArgumentNotNullOrEmpty( appConfigFilePath, "appConfigFilePath" );

            return this.TryGetPredefinedConnections( appConfigFilePath, out predefinedConnections, throwOnError: false );
        }

        #endregion
    }
}
